/*
 * Copyright (c) 2010, Christian Lerche
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Author: Christian Lerche <christian.lerche@uni-rostock.de>
 *
 */

#include "sendm.h"

/* Debug section as you will find it in every uDPWS file */
#define DEBUG                 UDPWS_DBG_LEVEL_SENDM  /* 0 - No Output -> 5 - Full Debug */
#define UDPWS_DEBUG_MODULE    "SENDM"
#include "udpws-debug.h"

/* Implementation of the "sending machine" */

//struct sendm_s sm;

void sendm_init(struct sendm_s *sm, void* mem, int max_items) {
    DBG_PRINT_VERBOSE("sendm_init()");
    sm->itmes = (struct send_header_s*) mem;
    sm->max_items = max_items;
    sm->first = -1;
    sm->used = 0;
    sm->state = SENDM_STATE_IDLE;
    sm->error = 0;
    sm->ack_len = 0;
}

int sendm_add(struct sendm_s *sm, const char buf[], uint16_t buf_len, int in_front
#ifdef AVR
, int8_t in_progmem
#endif /* AVR */
) {
    struct send_header_s *item;
    struct send_header_s *prev;
    uint8_t index;

    DBG_PRINT_VERBOSE("add %i bytes", buf_len); DBG_PRINT_WARNING_CHECK(sendm_started(sm), "try to add a new item after the sending machine was started"); DBG_PRINT_WARNING_CHECK(sendm_finished(sm), "try to add a new item after the sending machine finished");

    /* check if free headers available */
    if (sm->used == sm->max_items) {
        sm->error = 1;
        return SENDM_ERR;
DBG_PRINT_ERROR    ("sendm_add() failed! No space available!")
}

/* get new header */
index = sm->used++;
item = &sm->itmes[index];
item->buf = (char*)buf;
item->buf_len = buf_len;
#ifdef AVR
item->in_progmem = in_progmem;
#endif /* AVR */

/* check if this is the first header */
if (sm->first == -1) {
    sm->first = index;
    sm->last = index;
    item->next = -1;
} else if (in_front) {
    /* in front of list*/
    item->next = sm->first;
    sm->first = index;
} else {
    /* at the end of list */
    prev = &sm->itmes[sm->last];
    prev->next = index;
    /*NOTE: there might be a bug in the msp430 compiler because
     * these commands lead to wrong results (only for msp430-gcc):
     * (&sm->itmes[sm->last])->next = index;
     * sm->itmes[sm->last].next = index;
     * therefore we need the "prev" temp variable*/
    item->next = -1;
    sm->last = index;
}
return SENDM_OK;
}

void sendm_get_send_buf(struct sendm_s *sm, char* buf, uint16_t len) {
    uint16_t len_send = 0;
    uint16_t len_rest = len;
    struct send_header_s *item;

    DBG_PRINT_VERBOSE("get %i bytes", len);

    if (sm->first == -1 || sm->error) {
        sm->ack_len = 0;
        return;
    }
    item = &sm->itmes[sm->first];
    do {
        len_send = item->buf_len > len_rest ? len_rest : item->buf_len;
#ifdef AVR
        if (item->in_progmem) {
            memcpy_P(buf, item->buf, len_send);
        } else {
            memcpy(buf, item->buf, len_send);
        }
#else
        memcpy(buf, item->buf, len_send);
#endif /* AVR */
        buf += len_send;
        len_rest -= len_send;
        if (item->next == -1) {
            break;
        } else {
            item = &sm->itmes[item->next];
        }
    } while (len_rest);
    len -= len_rest;
    /* we need to wait for an ack */
    sm->ack_len = len;
}

void sendm_ack_buf(struct sendm_s *sm) {
    struct send_header_s *item;
    uint16_t ack_len = sm->ack_len;
    DBG_PRINT_VERBOSE("ack %i bytes", ack_len);

    /* nothing to ack anymore after we finished */
    sm->ack_len = 0;

    if (sm->first == -1) {
        DBG_PRINT_WARNING("try to ack whereas the sendm is empty");
        return;
    }
    item = &sm->itmes[sm->first];
    while (1) {
        if (ack_len >= item->buf_len) {
            ack_len -= item->buf_len;
            sm->first = item->next;
            if (sm->first == -1) {
                DBG_PRINT_WARNING_CHECK(ack_len > 0, "tried to ack more than available");
                sm->state = SENDM_STATE_FINISHED;
                break;
            } else {
                item = &sm->itmes[item->next];
            }
        } else {
            /*ack_len < item->buflen*/
            item->buf += ack_len;
            item->buf_len -= ack_len;
            break;
        }
    }
}

int sendm_get_len(struct sendm_s *sm) {
    struct send_header_s *item;
    int len = 0;
    if (sm->first == -1 || sm->error) {
        return -1;
    }
    item = &sm->itmes[sm->first];
    while (1) {
        len += item->buf_len;
        if (item->next == -1) {
            break;
        }
        item = &sm->itmes[item->next];
    }
    return len;
}

/*TODO: this functions are deprecated because they work on an global unique struct
 * in the newer version we have a sendm handle */
#if DEBUG > 4 /* Debug level is set to ALL */
void sendm_print(struct sendm_s *sm) {
    int len = sendm_get_len();
    if (len < 0) {
        DBG_PRINT_VERBOSE("sendm error or sendm is empty!");
        return;
    }
    char* buf = malloc(len+1);
    sendm_get_buf(buf, &len);
    DBG_PRINT_VERBOSE("SENDM:\n");
    print_buf(buf, len);
    DBG_PRINT_VERBOSE("\n");
}

void sendm_print_list() {
    //TODO
}

void sendm_test_function() {
    uint16_t len = 0;
    int ack_toggle = 0;
    char* a = "0123456789"; //10
    char* b = "abcdefghijkl"; //12
    char* c = "xyz"; //3
    sendm_init();
    sendm_add_top_ram(a, 10);
    sendm_add_end_ram(b, 12);
    sendm_add_top_ram(c, 3);

    while(!sendm_finished()) {
#if 0
        char* buf;
        len = 2;
        sendm_get_item(&buf, &len);
        DBG_PRINT_VERBOSE("sendm_get (%i): ", len);
        print_buf(buf, len);
        DBG_PRINT_VERBOSE("\n");
        if (ack_toggle) {
            sendm_ack_item(len);
            ack_toggle = 0;
        } else ack_toggle = 1;
#else
        char buf[100];
        len = 4;
        sendm_get_buf(buf, &len);
        DBG_PRINT_VERBOSE("sendm_get_buf (%i): ", len);
        print_buf(buf, len);
        DBG_PRINT_VERBOSE("\n");

        if (ack_toggle) {
            sendm_ack_buf(len);
            ack_toggle = 0;
        } else ack_toggle = 1;
#endif /*0*/
    }
    exit(0);

}
#endif /* DEBUG > 4*/

/* Old functions that are not longer used */
#if 0
void sendm_get_item(char** buf, uint16_t* len) {
    struct send_header_s *item;
    if (sm.first != -1) {
        item = &sm.itmes[sm.first];
        if (*len > item->buf_len) {
            *len = item->buf_len;
        }
        *buf = item->buf;
    } else {
        /* thats the way how we show that there is nothing anymore */
        *buf = NULL;
        *len = 0;
        DBG_PRINT_WARNING("try to get something but there is nothing anymore!");
    }
}

void sendm_ack_item(uint16_t len) {
    struct send_header_s *item;
    if (sm.first != -1) {
        item = &sm.itmes[sm.first];
        DBG_PRINT_WARNING_CHECK(len > item->buf_len, "cannot ack more than the rest of one item!\n");

        if (len >= item->buf_len) {
            /* this item was sent, take the next one */
            sm.first = item->next;
            if (sm.first == -1) {
                /* finish */
                sendm_init();
                sm.finished = 1;
            }
        } else {
            /* only a part of the current item was acked */
            item->buf+=len;
            item->buf_len-=len;
        }
    } else {
        /* This should never happen! */
        DBG_PRINT_WARNING("try to ack something but there is nothing anymore!");
    }
}
#endif /*0*/

